package com.opencee.common.security.oauth2;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import com.alibaba.fastjson.JSON;
import com.opencee.common.security.SecurityConstants;
import com.opencee.common.security.SecurityUser;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.util.StringUtils;

import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 自定义jwt认证用户信息转换器
 * 使用aes加密，解密用户信息
 *
 * @author liuyadu
 */
@Slf4j
public class CustomJwtUserAuthenticationConverter extends DefaultUserAuthenticationConverter {

    private String aesKey;

    public CustomJwtUserAuthenticationConverter(String aesKey) {
        this.aesKey = aesKey;
    }

    /**
     * 转换为自定义信息
     *
     * @param map
     * @return
     */
    private Object converter(Map<String, ?> map) {
        SecurityUser securityUser = null;
        if (map.get(SecurityConstants.PRIVATE_CLAIMS) != null) {
            // 随机生成密钥
            byte[] key = aesKey.getBytes();
            // 构建
            AES aes = SecureUtil.aes(key);
            // 解密
            String decryptStr = aes.decryptStr(map.get(SecurityConstants.PRIVATE_CLAIMS).toString());
            securityUser = JSON.parseObject(decryptStr, SecurityUser.class);
            String clientId = (String) map.get(DefaultAccessTokenConverter.CLIENT_ID);
            securityUser.setClientId(clientId);
        }
        return securityUser;
    }

    /**
     * 转换用户
     *
     * @param authentication
     * @return
     */
    @Override
    public Map<String, ?> convertUserAuthentication(Authentication authentication) {
        Map<String, Object> response = new LinkedHashMap();
        Object principal = authentication.getPrincipal();
        if (principal != null) {
            String payload = JSON.toJSONString(principal);
            // 随机生成密钥
            byte[] key = aesKey.getBytes();
            // 构建
            AES aes = SecureUtil.aes(key);
            // 加密为16进制表示
            String encryptHex = aes.encryptHex(payload);
            // 放入私密信息
            response.put(SecurityConstants.PRIVATE_CLAIMS, encryptHex);
        }
        return response;
    }

    /**
     * 读取认证信息
     *
     * @param map
     * @return
     */
    @Override
    public Authentication extractAuthentication(Map<String, ?> map) {
        if (map.containsKey(SecurityConstants.PRIVATE_CLAIMS)) {
            Object principal = converter(map);
            Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
            if (principal != null) {
                UserDetails user = (UserDetails) principal;
                authorities = user.getAuthorities();
            }
            return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
        }
        return null;
    }

    /**
     * 查询权限
     *
     * @param map
     * @return
     */
    private Collection<? extends GrantedAuthority> getAuthorities(Map<String, ?> map) {
        if (!map.containsKey(AUTHORITIES)) {
            return AuthorityUtils.NO_AUTHORITIES;
        }
        Object authorities = map.get(AUTHORITIES);
        if (authorities instanceof String) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList((String) authorities);
        }
        if (authorities instanceof Collection) {
            return AuthorityUtils.commaSeparatedStringToAuthorityList(StringUtils
                    .collectionToCommaDelimitedString((Collection<?>) authorities));
        }
        throw new IllegalArgumentException("Authorities must be either a String or a Collection");
    }

}
